#ifndef PRIMITIVE_H
#define PRIMITIVE_H

#include <vector>
#include <GL/gl.h>
#include <GL/glut.h>

#define VERTEX     01
#define EDGE       02
#define FACE       03
#define OBJECT     04
#define ENVIROMENT 05

using namespace std;

class Point{
    protected:
        float x;
        float y;
        float z;

    public:
        Point();
        Point(float _x, float _y, float _z = 0.0);
        ~Point();
            
        void setX(float x);
        float getX();
        
        void setY(float y);
        float getY();
        
        void setZ(float z);
        float getZ();       

        void show();
        
        float lenght();
        
        float distance(Point &p);
        
        bool operator==(Point &p);
        
        void operator+=(Point p);
        Point operator+(Point p);
        
        void operator-=(Point p);
        Point operator-(Point p);
        
        void operator*=(float h);
        Point operator*(float h);
        
        void operator/=(float h);
        Point operator/(float h);
        
        Point crossProduct(Point &p);
        float dotProduct(Point &p);
        Point normalize();
};


class Model{
    public:
        static void drawSphere(float radius, int slices = 25, int stacks = 15);
        static void drawSphere(Point p, float radius);
        static void drawSegment(Point origin, Point destination);
};


class Primitive{
    protected:
        unsigned int parentId;
        unsigned int id;
        
        int type;
        
        float r,g,b,a;
        int size;
        
        float rotate_x;
        float rotate_y;
        float rotate_z;

        float translate_x;
        float translate_y;
        float translate_z;

        float scale_x;
        float scale_y;
        float scale_z;        
        
    public:
         Primitive();
         ~Primitive();

        int getParentId();
        int getId();
        
        void setColor(float _r, float _g, float _b, float _a = 1.0);
        float getRed();
        float getGreen();
        float getBlue();
        float getAlpha();
        void setSize(int s);
        int getSize();
        
        void setRotateX(float rx);
        float getRotateX();
        void setRotateY(float ry);
        float getRotateY();
        void setRotateZ(float rz);
        float getRotateZ();
        void rotate();
        void rotate(float angle, Point direction);
        
        void setTranslation(float tx, float ty, float tz);
        void setTranslateX(float tx);
        float getTranslateX();
        void setTranslateY(float ty);
        float getTranslateY();
        void setTranslateZ(float tz);
        float getTranslateZ();
        void translate();
        
        void setScaleX(float sx);
        float setScaleX();
        void setScaleY(float sy);
        float setScaleY();
        void setScaleZ(float sz);
        float setScaleZ();
        
        void transforms();
        
        virtual void draw(){}
};


class Vertex : public Primitive, public Point{
    public:
        Vertex(unsigned int _id = 0, unsigned int _parentId = 0);
        Vertex(float _x, float _y, unsigned int _id = -1, unsigned int _parentId = 0);
        Vertex(float _x, float _y, float _z, unsigned int _id=0, unsigned int _parentId=0);
        ~Vertex();
        
        bool isEmpty();
        
        void draw(bool showVertex = true);
};

class Edge : public Primitive{
    private:
        pair<Vertex, Vertex> vertices;

    public:
        Edge(Vertex v1, Vertex v2, unsigned int _id = 0, unsigned int _parentId = 0);
        Edge(unsigned int _id = 0, unsigned int _parentId = 0);
        ~Edge();
        
        bool isEmpty();
        void show();
        
        void setVertices(Vertex v1, Vertex v2);
        pair<Vertex, Vertex> &getVertices();
        
        void draw(bool showEdge = true, bool showVertices = true);
};

class Face : public Primitive{
    private:
        vector<Edge> edges;

    public:
        Face(unsigned int _id = 0, unsigned int _parentId = 0);
        ~Face();

        bool isEmpty();
        void show();
        
        void addEdge(Edge e);
        bool hasEdge(int _id);
        Edge& getEdge(unsigned int _id);
        vector<Edge> &getEdges();
        
        void draw(bool showFace = true, bool showEdges = true, bool showVertices = true);
};

class Object : public Primitive{
    private:
        vector<Face> faces;

    public:
        Object(unsigned int _id = 0);
        ~Object();
        
        bool isEmpty();
        void show();
        
        bool hasVertex(int _id);
        Vertex& getVertex(unsigned int _id);
        
        bool hasEdge(int _id);
        Edge& getEdge(unsigned int _id);
        
        void addFace(Face &f);
        bool hasFace(int _id);
        Face& getFace(unsigned int _id);        
        vector<Face>& getFaces();
        
        void draw(bool showFaces = true, bool showEdges = true, bool showVertices = true);
};

class ObjectFactory{
    private:
        unsigned int vertexId;
        unsigned int edgeId;
        unsigned int faceId;
        unsigned int objectId;

    public:
        ObjectFactory();
        ~ObjectFactory();
        
        Vertex createVertex(Point p, unsigned int _parentId = 0);
        Vertex createVertex(float x, float y, float z, unsigned int _parentId = 0);
        
        Edge createEdge(Vertex &v1, Vertex &v2, unsigned int _parentId = 0);
        
        Face createFace(vector<Edge> &edges, unsigned int _parentId = 0);
        Face createFace(unsigned int _parentId = 0);
        Face createFace(vector<Point> points, unsigned int _parentId = 0);

        Object createObject();
};

#endif /* PRIMITIVE_H */
